package com.small.site.exception;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.client.ClientException;
import com.small.site.util.R;
import io.netty.channel.ConnectTimeoutException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * @author  xiaochi
 * @date    2021/11/21 13:38
 * @desc    网关本身全局异常获取（不能获取网关调用（转发）的项目发生异常）
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Primary   // 同类型带有该注解的 Bean 优先背识别
@Component
public class JsonExceptionHandler implements WebExceptionHandler {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 所有异常都会在这里被集中处理
     * @param exchange 上下文
     * @param ex       异常
     * SuppressWarnings("NullableProblems")：屏蔽 @NonNullApi 的警告
     */
    @Override
    @SuppressWarnings("NullableProblems")
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        Throwable cause = ex.getCause();
        log.error("网关错误：{}==={}===={}", ex.getMessage(), this.buildMessage(request, ex), ex.getStackTrace());
        R<Object> r;
        if (cause instanceof ResponseStatusException) {
            r = R.error("当前访问的地址不存在，请修改后重试");
        }else if(ex instanceof ConnectTimeoutException){
            r = R.error("服务连接超时，请稍后");
        }else if(ex instanceof ClientException){
            r = R.error("服务正在上线，请稍后");
        }else if(ex instanceof NotFoundException){
            r = R.error("服务未被发现,正在努力寻找,请稍后");
        }else if(ex instanceof ResponseStatusException ){
            r = R.error("当前访问的地址不存在，请修改后重试");
        }else if (ex instanceof RuntimeException){
            return response.writeWith(Mono.fromSupplier(() -> {
                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                return response.bufferFactory().wrap(ex.getMessage().getBytes());
            }));
        }else {
            r = R.error("服务异常");
        }
        return response.writeWith(Mono.fromSupplier(() -> {
            byte[] bytes;
            try {
                bytes = objectMapper.writeValueAsBytes(r);
            } catch (JsonProcessingException e) {
                response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                bytes = "JSON 转换异常！".getBytes();
            }
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.bufferFactory().wrap(bytes);
        }));
    }

    /**
     * 构建异常信息
     * @param request
     * @param ex
     * @return
     */
    private String buildMessage(ServerHttpRequest request, Throwable ex) {
        StringBuilder message = new StringBuilder("Failed to handle request [");
        message.append(Objects.requireNonNull(request.getMethod()).name());
        message.append(" ");
        message.append(request.getURI().getPath());
        message.append("]");
        if (ex != null) {
            message.append(": ");
            message.append(ex.getMessage());
        }
        return message.toString();
    }
}